package com.xc.stock.infrastructure.redis;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import com.google.common.base.Stopwatch;
import com.google.common.collect.Maps;
import com.xc.stock.aop.distributedlock.DistributedLockCallback;
import com.xc.stock.aop.distributedlock.DistributedLockInfo;
import com.xc.stock.aop.distributedlock.DistributedLockTemplate;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * title: SingleDistributedLockTemplate.java 
 *
 * @author xlfd
 * @email xlfd@gmail.com
 * @version 1.0  
 * @created Sep 1, 2020 5:03:20 PM
 */
@Slf4j
public class SingleDistributedLockTemplate implements DistributedLockTemplate {
	private RedissonClient redisson;
	DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS");

    public SingleDistributedLockTemplate() {
    }

    public SingleDistributedLockTemplate(RedissonClient redisson) {
        this.redisson = redisson;
    }

    @Override
	public <T> T lock(DistributedLockCallback<T> callback, DistributedLockInfo... lockInfoList) throws Throwable {
		Stopwatch stopwatch = Stopwatch.createStarted();
		Map<RLock, DistributedLockInfo> lockMap = Maps.newConcurrentMap();
		
		for (DistributedLockInfo info : lockInfoList) {
			RLock lock = getLock(info.getLockName(), info.getFairLock());
			lockMap.put(lock, info);
		}
		
		try {
			for (Entry<RLock, DistributedLockInfo> entry : lockMap.entrySet()) {
				RLock lock = entry.getKey();
				DistributedLockInfo info = entry.getValue();
				log.info("{}-准备获取{}分布式锁:{}", dateTimeFormatter.format(LocalDateTime.now()), info.getMessage(), info.getLockName());
				lock.lock(info.getLeaseTime(), info.getTimeUnit());
				log.info("{}-{}分布式锁:{}获取成功, 耗时:{} ms", dateTimeFormatter.format(LocalDateTime.now()), info.getMessage(), info.getLockName(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
			}

			return callback.process();
		} finally {
			for (Entry<RLock, DistributedLockInfo> entry : lockMap.entrySet()) {
				RLock lock = entry.getKey();
				DistributedLockInfo info = entry.getValue();
				if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
					lock.unlock();
					log.info("{}-{}分布式锁:{}释放成功, 耗时:{} ms", dateTimeFormatter.format(LocalDateTime.now()), info.getMessage(), info.getLockName(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
				}
			}

		}
	}

    private RLock getLock(String lockName, boolean fairLock) {
        RLock lock;
        if (fairLock) {
            lock = redisson.getFairLock(lockName);
        } else {
            lock = redisson.getLock(lockName);
        }
        return lock;
    }

    public void setRedisson(RedissonClient redisson) {
        this.redisson = redisson;
    }
}
